'This class runs inside the main
'server to store all user info
Imports System.Net
Imports System.Net.Sockets
Imports System.Text.RegularExpressions
Imports System.Threading
''' -----------------------------------------------------------------------------
''' Project	 : Server
''' Class	 : clsUser
''' 
''' -----------------------------------------------------------------------------
''' <summary>
''' 
''' </summary>
''' <remarks>
''' </remarks>
''' <history>
''' 	[Jason Schreckengost]	6/25/2006	Created
''' </history>
''' -----------------------------------------------------------------------------
Public Class clsUser
#Region "Variables"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private iID As Integer
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private ipIP As String
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private sktRef As New Socket(AddressFamily.InterNetwork, SocketType.Stream, _
        ProtocolType.Tcp)
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private ASCII As New System.Text.ASCIIEncoding
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private sName As String
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private sUsername As String
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private iUID As Integer
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private sMessage As String
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private sqlConnection As clsMySQL
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private bLogged As Boolean
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private sPassword As String
    Private thdRcvdCallback As Thread
    Private bClosing As Boolean
    Const SyncRoot = "_"
#End Region
#Region "Enumerations"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Enum eStatus
        Offline = 0
        Online
        Busy
        Invisible
        BeRightBack
        Away
        DoNotDisturb
    End Enum
#End Region
#Region "Constructor/Destructor"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jas]	7/18/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub New(ByVal id As Int32)
        sqlConnection = New clsMySQL(id)
        iID = id
        bClosing = False
    End Sub
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jas]	7/18/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Protected Overrides Sub Finalize()
        SyncLock Me.SyncRoot
            bClosing = True
            If Not thdRcvdCallback Is Nothing Then
                Try
                    If thdRcvdCallback.ThreadState = ThreadState.Running Then thdRcvdCallback.Abort()
                Catch
                    Display("An Error has occured in the Finalize() Method of User # " + iID.ToString _
                    + vbNewLine + "Skipping statement...")
                End Try
            End If
            iID = Nothing
            ipIP = Nothing
            If Not sktRef Is Nothing Then
                If sktRef.Connected Then
                    sktRef.Shutdown(SocketShutdown.Both)
                End If
                sktRef.Close()
            End If
            sktRef = Nothing
            ASCII = Nothing
            sName = Nothing
            sUsername = Nothing
            iUID = Nothing
            sMessage = Nothing
            If Not sqlConnection Is Nothing Then
                If sqlConnection.Connected Then
                    sqlConnection.Close()
                End If
            End If
            sqlConnection = Nothing
            bLogged = Nothing
            sPassword = Nothing
            bClosing = Nothing
        End SyncLock
    End Sub
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jas]	7/26/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub Close()
        If Not sUsername Is Nothing Then
            sqlConnection.friendBroadcast(Me, eStatus.Offline)
            htUsers.Remove(sUsername)
            sqlConnection.MySQLCommand("UPDATE `users` SET `logged`=NULL WHERE `UID`=" + iUID.ToString)
        End If
        Me.Finalize()
    End Sub
#End Region
#Region "Properties"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property ConID() As Integer
        Get
            ConID = iID
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property Message() As String
        Get
            Message = sMessage
        End Get
        Set(ByVal Value As String)
            sMessage = Value
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property UID() As Integer
        Get
            UID = iUID
        End Get
        Set(ByVal Value As Integer)
            iUID = Value
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property Username() As String
        Get
            Username = sUsername
        End Get
        Set(ByVal Value As String)
            sUsername = Value
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property SocketRef() As Socket
        Get
            SocketRef = sktRef
        End Get
        Set(ByVal Value As Socket)
            sktRef = Value
            Me.setIP = sktRef.RemoteEndPoint
            'Dim lopt As LingerOption = New LingerOption(False, 0)
            'sktRef.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.DontLinger, Nothing)
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public ReadOnly Property IP() As String
        Get
            IP = ipIP
        End Get
    End Property
    Public ReadOnly Property Connected() As Boolean
        Get
            If Not sktRef Is Nothing Then
                Dim null(0) As Byte
                Try
                    sktRef.Send(null, 0, 0)
                    Connected = True
                Catch ex As Exception
                    Connected = False
                End Try
            Else
                Connected = False
            End If
        End Get
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Private WriteOnly Property setIP() As IPEndPoint
        Set(ByVal Value As IPEndPoint)
            ipIP = Value.Address.ToString + ":" + Value.Port.ToString
        End Set
    End Property
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Property Name() As String
        Get
            Name = sName
        End Get
        Set(ByVal Value As String)
            sName = Value
        End Set
    End Property
#End Region
#Region "Events"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="user"></param>
    ''' <param name="data"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Event DataReceived(ByVal user As clsUser, ByVal data As String)
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="user"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Event DataSent(ByVal username As String, ByVal data As String)
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="user"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Event LoggedIn(ByVal user As clsUser)
#End Region
#Region "Methods"
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="ar"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub recvCallback(ByVal ar As IAsyncResult)
        thdRcvdCallback = Thread.CurrentThread
        If Thread.CurrentThread.Name Is Nothing Then Thread.CurrentThread.Name = "clsUser.recvCallback"
        Dim numbytes As Int32, bytes As Byte()
        'If sqlConnection Is Nothing And Not bClosing Then
        'sqlConnection = New clsMySQL(ConID)
        'End If
        Try
            bytes = CType(ar.AsyncState, Byte())
            If Not sktRef Is Nothing Then numbytes = sktRef.EndReceive(ar)
        Catch
            Display("An Error occured in socket #" + ConID.ToString + "." + vbNewLine _
                + "Closing the socket to prevent a crash." + vbNewLine _
                + "This error occurded in the recvCallback function.")
            sktRef.Shutdown(SocketShutdown.Both)
            sktRef.Close()
            sktRef = Nothing
            'Me.Close()
            'Threading.Thread.CurrentThread.Abort()
            Exit Sub
        End Try
        If numbytes = 4094 Then
            numbytes = 0
            For Each bit As Byte In bytes
                If bit > 0 Then
                    numbytes += 1
                Else
                    Exit For
                End If
            Next
        End If
        If numbytes = 0 Then
            Try
                Users(iID).Close()
                Exit Sub
            Catch ex As Exception
                MsgBox("Error closing User #" + iID.ToString)
            End Try
        End If
        Dim data As String
        If Not ASCII Is Nothing Then data = ASCII.GetString(bytes, 0, numbytes)
        Array.Clear(bytes, 0, bytes.Length)
        RaiseEvent DataReceived(Me, data)
        Array.Clear(bytes, 0, bytes.Length)
        Dim re As Regex = New Regex("[A-Z]+_?")
        Select Case re.Match(data).ToString
            Case "LOGIN_"
                Dim tmpUser As String = data.Substring(6, data.IndexOf(" ") - 6)
                Dim tmpPass As String
                If data.Length - (6 + tmpUser.Length) > 0 Then tmpPass = data.Substring(data.IndexOf(" ") + 1, 40)
                If CheckData(tmpUser, "[a-zA-Z0-9_.]+") And _
                 CheckData(tmpPass, "[a-f0-9]") Then
                    If sqlConnection.rowsQuery("SELECT COUNT(*) FROM `users` WHERE" _
                     + "`UN`='" + tmpUser.ToLower + "' AND `PW`='" _
                     + tmpPass.ToLower + "' AND logged is null") Then
                        sUsername = tmpUser
                        sPassword = tmpPass
                        htUsers.Add(sUsername, iID)
                        sqlConnection.MySQLCommand("UPDATE users SET logged=" _
                         + iID.ToString + " WHERE UN='" + sUsername + "'")
                        bLogged = True
                        sqlConnection.setUserInfo(Me)
                        RaiseEvent LoggedIn(Me)
                        Dim txtToSend As String = "LOGINACCEPT_"
                        txtToSend += sqlConnection.parseFriends(Me.UID)
                        txtToSend += sqlConnection.parseOfflines(Me.UID)
                        txtToSend += sqlConnection.parseGroups(Me.UID)
                        Me.Send(txtToSend)
                        sqlConnection.friendBroadcast(Me, eStatus.Online)
                    Else
                        Send("LOGINDENY: Bad Username and/or Password")
                    End If
                End If
            Case "MSGTO_"
                Dim reUsername As Regex = New Regex("_[a-zA-Z0-9._]+:")
                'Dim reMsg As Regex = New Regex(":*")
                Dim sTo As String = reUsername.Match(data.Substring(5)).ToString
                sTo = sTo.Substring(1, sTo.Length - 2)
                Dim sMsg As String = data.Substring(data.IndexOf(":"), data.Length - data.IndexOf(":"))
                Dim index As Int32 = IIf(htUsers.ContainsKey(sTo), htUsers.Item(sTo), -1)
                'do Message Code
                'Change up the encryption here
                'SEND=MSGFROM_<me.username>:<message>
                If index < 0 Then
                    sqlConnection.setOffline(sqlConnection.getUID(sTo), Me.iUID, sMsg.Substring(1))
                    'MsgBox("no user found")
                    'MsgBox("MSGFROM_" + Me.Username + sMsg)
                Else 'Send the message on to the recipient

                    Users(index).Send("MSGFROM_" + Me.Username + sMsg)
                End If
            Case "LOGOUT", "DISCONNECT"
                'data.Substring(0, 10) = "DISCONNECT" Then
                sktRef.Shutdown(SocketShutdown.Both)
                sktRef.Close()
                sktRef = Nothing
                'Me.Close()
            Case "VERSION_"
                Send("UPTODATE")
            Case "SEARCHFOR_"
                Dim result As String = sqlConnection.parseFriendSearch(data.Substring(10), Me.iUID)
                Send(result)
            Case "ADDFRIEND_"
                Do Until data.IndexOf("ADDFRIEND_") = -1 And data.Length = 0
                    data = data.Substring(10)
                    Me.sqlConnection.MySQLCommand("INSERT INTO `friends`(`user`,`friend`) VALUES(" _
                      + Me.iUID.ToString + "," + sqlConnection.getUID(data.Substring(0, IIf(data.IndexOf("ADDFRIEND_") >= 0, data.IndexOf("ADDFRIEND_"), data.Length))).ToString + ")")
                    Send(sqlConnection.getFriendStatus(data.Substring(0, IIf(data.IndexOf("ADDFRIEND_") >= 0, data.IndexOf("ADDFRIEND_"), data.Length))))
                    If data.IndexOf("ADDFRIEND_") >= 0 Then
                        data = data.Substring(data.IndexOf("ADDFRIEND_"))
                    Else
                        data = ""
                    End If
                Loop
        End Select
        Try
            sktRef.BeginReceive(bytes, 0, bytes.Length, _
            SocketFlags.None, AddressOf recvCallback, bytes)
        Catch
            Display("SERVER: It appears that user(" + Me.iID.ToString + ") dropped " _
            + "without a LOGOUT or DISCONNECT signal." + vbNewLine _
            + "SERVER: Closing and releasing user(" + Me.iID.ToString + ")")
            'Me.Close()
            If Not sktRef Is Nothing Then
                sktRef.Shutdown(SocketShutdown.Both)
                sktRef.Close()
                sktRef = Nothing
            End If
        End Try
    End Sub
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="data"></param>
    ''' <param name="search"></param>
    ''' <returns></returns>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Function CheckData(ByVal data As String, ByVal search As String) As Boolean
        Dim RegEx As New System.Text.RegularExpressions.Regex(search)
        Return RegEx.IsMatch(data)
    End Function
    ''' -----------------------------------------------------------------------------
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="data"></param>
    ''' <remarks>
    ''' </remarks>
    ''' <history>
    ''' 	[Jason Schreckengost]	6/25/2006	Created
    ''' </history>
    ''' -----------------------------------------------------------------------------
    Public Sub Send(ByVal data As String)
        Dim bytes() As Byte = ASCII.GetBytes(data)
        sktRef.Send(bytes)
        RaiseEvent DataSent(Me.Username, data)
    End Sub
    Public Sub Send(ByVal data() As Byte)
        'Dim bytes() As Byte = ASCII.GetBytes(data)
        sktRef.Send(data)
        'RaiseEvent DataSent(Me.Username, data)
    End Sub
#End Region
End Class